package com.zuoban.config;

import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zuoban.entity.sys.SysLog;
import com.zuoban.log.Subject;

import com.zuoban.util.ThreadMdcUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import shade.jetbrains.annotations.NotNull;


import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;


@Configuration
@EnableConfigurationProperties(value = ThreadPoolProperties.class)
@EnableAsync
@RequiredArgsConstructor
@Slf4j
public class ThreadConfig extends ThreadPoolTaskExecutor implements AsyncConfigurer {

    private final ThreadPoolProperties threadPoolProperties;
    private final Subject subject;

    @SneakyThrows
    @Bean("asyncTaskExecutor")
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(threadPoolProperties.getCorePoolSize());
        executor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize());
        executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());
        executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
        executor.setRejectedExecutionHandler(threadPoolProperties.getRejectExecutorHandler().newInstance());
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (throwable, method, objects) -> {
            SysLog sysLog = new SysLog();

            String msg = StringUtils.EMPTY;
            if (ArrayUtils.isNotEmpty(objects) && objects.length > 0) {
                msg = String.join(msg, "参数是：");
                for (Object object : objects) {
                    msg = String.join(msg, String.valueOf(object));
                }
            }
            msg = String.join(msg, throwable.getMessage());
            sysLog.setInfo(msg);
            subject.notifyObserver(null,null,sysLog);
        };
    }

    @Override
    public void execute(@NotNull Runnable task) {
        super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }


    @Override
    public <T> Future<T> submit(@NotNull Callable<T> task) {
        return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }

    @Override
    public Future<?> submit(@NotNull Runnable task) {
        return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
    }
}
